﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.WebControls;
using System.Reflection;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using System.Web;
using System.Web.Caching;

namespace UserLimitedGroupSelection.Code
{
    public class UserLimitedGroupSelectionQuery : SimpleQueryControl
    {
        /// <summary>
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreRender(EventArgs e)
        {
            ColumnList.Visible = false;

            base.OnPreRender(e);
        }

        /// <summary>
        /// Issues the query.
        /// </summary>
        /// <param name="search">The search.</param>
        /// <param name="groupName">Name of the group.</param>
        /// <param name="providerID">The provider ID.</param>
        /// <param name="hierarchynodeID">The hierarchynode ID.</param>
        /// <param name="pageIndex">Index of the page.</param>
        /// <param name="pageSize">Size of the page.</param>
        /// <returns></returns>
        protected override int IssueQuery(string search, string groupName, string providerID, string hierarchynodeID, int pageIndex, int pageSize)
        {
            //Search AD and parse the results
            //Logic copied from PeopleQueryControl
            bool flag = false;

            List<PickerEntity> list = UserLimitedGroupSelectionQuery.SearchAD(search, pageSize, UserLimitedGroupSelectionEditor.GetCurrentField((UserLimitedGroupSelectionEditor)PickerDialog.EditorControl));

            List<SPProviderHierarchyTree> list2 = new List<SPProviderHierarchyTree>();
            list2.Add((SPProviderHierarchyTree)typeof(PeopleQueryControl).GetMethod("GetPlaceholderTree", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { SPResource.GetString("AllSearch", new object[0]), list, "0" }));

            //Add results to the dialog window
            base.PickerDialog.SetResults(list2.ToArray());

            if (list.Count == 0)
                base.PickerDialog.ErrorMessage = SPResource.GetString("PeoplePickerNoSearchResultError", new object[0]);

            if (flag && list.Count == pageSize)
                base.PickerDialog.ErrorMessage = SPResource.GetString("PeoplePickerTooManyResults", new object[0]);

            base.PickerDialog.ResultControl.PageSize = list.Count;

            return list.Count;
        }

        /// <summary>
        /// Searches the AD.
        /// </summary>
        /// <param name="web">The web.</param>
        /// <param name="input">The input.</param>
        /// <param name="scopes">The scopes.</param>
        /// <param name="sources">The sources.</param>
        /// <param name="maxCount">The max count.</param>
        /// <param name="reachMaxCount">if set to <c>true</c> [reach max count].</param>
        /// <returns></returns>
        internal static List<PickerEntity> SearchAD(string input, int maxCount, UserLimitedGroupSelection field)
        {
            //Method called to search AD

            //field.GroupName
            //field.ShowUsers
            //field.ShowGroups

            //Set principal type
            SPPrincipalType pt = SPPrincipalType.None;
            bool reachedMaxCount;
            List<PickerEntity> returnValue = new List<PickerEntity>();

            if (field.ShowUsers)
                pt |= SPPrincipalType.User;

            if (field.ShowGroups)
                pt |= SPPrincipalType.SecurityGroup;

            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (SPSite site = new SPSite(SPContext.Current.Site.ID))
                {
                    using (SPWeb web = site.OpenWeb(SPContext.Current.Web.ID))
                    {
                        IList<SPPrincipalInfo> results = SPUtility.SearchPrincipals(web, input, pt, SPPrincipalSource.All, null, maxCount, out reachedMaxCount);

                        if (results != null && results.Count > 0)
                        {
                            //Check if user is in specified group
                            if (!String.IsNullOrEmpty(field.GroupName))
                            {
                                IList<SPPrincipalInfo> results2 = GetPrincipalsInGroup(web, field.GroupName);

                                if (results2 != null && results2.Count > 0)
                                {
                                    //Cross reference users with users in group
                                    results = results.Intersect(results2, new UserLimitedGroupSelectionPrincipalInfoComparer()).ToList();

                                    if (results.Count == 0)
                                        results = null;
                                }
                                else
                                    results = null;
                            }

                            if(results != null && results.Count > 0)
                                foreach (SPPrincipalInfo pi in results)
                                    returnValue.Add(UserLimitedGroupSelectionEditor.ConvertPrincipalInfoToPickerEntity(pi));
                        }
                    }
                }
            });

            return returnValue;
        }

        /// <summary>
        /// Resolves the AD.
        /// </summary>
        /// <param name="web">The web.</param>
        /// <param name="input">The input.</param>
        /// <param name="scopes">The scopes.</param>
        /// <param name="sources">The sources.</param>
        /// <returns></returns>
        internal static PickerEntity ResolveAD(string input, UserLimitedGroupSelection field)
        {
            //Method called to search AD

            //field.GroupName
            //field.ShowUsers
            //field.ShowGroups

            //Set principal type
            SPPrincipalType pt = SPPrincipalType.None;
            PickerEntity returnValue = null;

            if (field.ShowUsers)
                pt |= SPPrincipalType.User;

            if (field.ShowGroups)
                pt |= SPPrincipalType.SecurityGroup;

            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (SPSite site = new SPSite(SPContext.Current.Site.ID))
                {
                    using (SPWeb web = site.OpenWeb(SPContext.Current.Web.ID))
                    {
                        SPPrincipalInfo result = SPUtility.ResolvePrincipal(web, input, pt, SPPrincipalSource.All, null, false);

                        if (result != null)
                        {
                            //Check if user is in specified group
                            if (!String.IsNullOrEmpty(field.GroupName))
                            {
                                IList<SPPrincipalInfo> results2 = GetPrincipalsInGroup(web, field.GroupName);

                                if (results2 != null && results2.Count > 0)
                                    //Cross reference user with users in group
                                    result = results2.Where(pi => pi.LoginName.Equals(result.LoginName, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
                                else
                                    result = null;
                            }

                            if(result != null)
                                returnValue = UserLimitedGroupSelectionEditor.ConvertPrincipalInfoToPickerEntity(result);
                        }
                    }
                }
            });

            return returnValue;
        }

        /// <summary>
        /// Gets the principals in group.
        /// </summary>
        /// <param name="web">The web.</param>
        /// <param name="groupName">Name of the group.</param>
        /// <returns></returns>
        internal static IList<SPPrincipalInfo> GetPrincipalsInGroup(SPWeb web, string groupName)
        {
            //Get group members
            IList<SPPrincipalInfo> returnValue = null;

            if (web != null && !String.IsNullOrEmpty(groupName))
            {
                //Check if the result was cached
                string cacheKey = String.Concat("ONPRVP.Intranet.Fields.Code.UserLimitedADSelectionQuery.GetPrincipalsInGroup_", web.ID, "_", groupName);
                returnValue = (IList<SPPrincipalInfo>)HttpContext.Current.Cache[cacheKey];

                if (returnValue == null)
                {
                    //Get members
                    bool reachedMaxCount;
                    returnValue = SPUtility.GetPrincipalsInGroup(web, groupName, int.MaxValue - 1, out reachedMaxCount);

                    if(returnValue != null)
                        HttpContext.Current.Cache.Add(cacheKey, returnValue, null, DateTime.Now.AddMinutes(10), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
                }
            }

            return returnValue;
        }
    }
}